Inside Macintosh: QuickTime Components

Previous | Chapter Top | Chapter Contents | Next

Performing Image Compression

This section describes what the Image Compression Manager does that affects compressors. It then provides sample code that shows how the compressor components prepare for image compression and how to compress an entire image or a horizontal band of an image.

When compressing an image, the Image Compression Manager performs three major tasks:

  1. The Image Compression Manager first determines which compressor is best able to compress the image. To do so, the Image Compression Manager examines the source image as well as the parameters specified by the application. If the application requested a specific compressor, the Image Compression Manager uses that compressor (unless it is not installed, in which case the Image Compression Manager returns an error to the application). If the application did not request a compressor, the Image Compression Manager chooses the compressor that will do the best job. The Image Compression Manager collects the information it needs to choose a compressor by issuing the CDPreCompress request to each qualifying compressor (see CDPreCompress for a detailed description of the CDPreCompress function).
  2. If the chosen compressor can handle the image directly, the Image Compression Manager passes the request through to the compressor. The compressor then processes the image and returns the compressed data to the specified location.
  3. If none of the compressors can handle it directly, the Image Compression Manager allocates an offscreen buffer and passes image bands to the compressor by issuing a CDBandCompress request. (For more on the CDBandCompress function, see CDBandCompress .) The compressor processes each band, accumulating the compressed data as it goes. When the image has been completely compressed, the Image Compression Manager returns control to the application.

Choosing a Compressor

Listing 1 shows how the Image Compression Manager calls the CDPreCompress function before an image is compressed. The compressor component returns information about how it is able to compress the image to the Image Compression Manager, so that it can fit the destination data to the requirements of the compressor component. This information includes compressor capabilities for

When your compressor component is called with the CDPreCompress function (described on CDPreCompress ), it can handle all aspects of the function itself, or only the most common ones. All image compressor components must handle at least one case.

Here is a list of some of the operations your compressor component can perform during compression. It describes parameters in the compression parameters structure (described on The Compression Parameters Structure ) and indicates the operations that are required and which flags in the compressor capabilities flags field of the compressor capabilities structure (described on The Compressor Capability Structure ) must be set to allow your compressor to handle them.

Listing 1 Preparing for simple compression operations

pascal long
CDPreCompress (Handle storage, register CodecCompressParams *p)
{
    CodecCapabilities *capabilities = p->capabilities;
/*
    First the compressor returns which depth input pixels it
    supports based on what the application has available. This
    compressor can only work with 32-bit input pixels.
*/  
    switch ( (*p->imageDescription)->depth ) {
        case 16:
            capabilities->wantedPixelSize = 32;
            break;
        default:
            return(codecConditionErr);
            break;
    }
    /*
        If the buffer gets banded, return the smallest one the
        compressor can handle.
    */  
    capabilities->bandMin = 2;
    /*
        If the buffer gets banded, return the increment
        by which it should increase.
    */
    capabilities->bandInc = 2;
        
    capabilities->extendWidth = (*p->imageDescription)->width & 1;
    capabilities->extendHeight = (*p->imageDescription)->height &
                                             1;

/*
    For efficiency, if the compressor could perform extension,
    these flags would be set to 0.
*/
    
    return(noErr);
}

Compressing a Horizontal Band of an Image

Listing 2 shows how the Image Compression Manager calls the CDBandCompress function when it wants the compressor to compress a horizontal band of an image.

This example does not perform compression on bands with a bit depth of more than 1 or an extension of width and height. If the example did do so, it would handle these cases faster.

Listing 2 Performing simple compression on a horizontal band of an image

pascal long
CDBandCompress (Handle storage, register CodecCompressParams *p)
{
    short                   width,height;
    Ptr                     cDataPtr,dataStart;
    short                   depth;
    Rect                    sRect;
    long                    offsetH,offsetV;
    Globals                 **glob = (Globals **)storage;
    register char           *baseAddr;
    long                    numLines,numStrips;
    short                   rowBytes;
    long                    stripBytes;
    char                    mmuMode = 1;
    register short          y;
    ImageDescription        **desc = p->imageDescription;
    OSErr                   result = noErr;
    
    /*
    If there is a progress function, give it an open call at
        the start of this band.
    */
    if (p->progressProcRecord.progressProc)
        p->progressProcRecord.progressProc (codecProgressOpen, 0,
            p->progressProcRecord.progressRefCon);

    width = (*desc)->width;
    height = (*desc)->height;
    depth = (*desc)->depth;
    dataStart = cDataPtr = p->data;

    /*
        Figure out offset to first pixel in baseAddr from the
        pixel size and bounds.
     */

    rowBytes = p->srcPixMap.rowBytes;
    sRect = p->srcPixMap.bounds;
    numLines = p->stopLine - p->startLine;              /* number of scan
                                                            lines */
    numStrips = (numLines+1)>>1;                        /* number of strips
                                                            in */
    stripBytes = ((width+1)>>1) * 5;
    
    /*
        Adjust the source baseAddress to be at the beginning
        of the desired rect.
    */

    switch ( p->srcPixMap.pixelSize ) {
    case 32:
        offsetH = sRect.left<<2;
        break;
    case 16:
        offsetH = sRect.left<<1;
        break;
    case 8:
        offsetH = sRect.left;
        break;

    /*
        This compressor does not handle the other cases directly.
    */

    default:
        result = codecErr;
        goto bail;
    }

    offsetV = sRect.top * rowBytes;
    baseAddr = p->srcPixMap.baseAddr + offsetH + offsetV;

    /*
        If there is not a data-unloading function,
        adjust the pointer to the next band.
    */
    
    if ( p->flushProcRecord.flushProc == nil ) {
        cDataPtr += (p->startLine>>1) * stripBytes;
    }
    else { /*
                 Make sure the compressor can deal with the
                 data-unloading function in this case.
            */
        if ( p->bufferSize < stripBytes ) {
            result = codecSpoolErr;
            goto bail;
        }
    }
    /*
        Perform the slower data-loading or progress operation, as
        required.
    */
    
    if ( p->flushProcRecord.flushProc ||
        p->progressProcRecord.progressProc ) {

        SharedGlobals *sg = (*glob)->sharedGlob;

        for ( y=0; y < numStrips; y++) {
            SwapMMUMode(&mmuMode);
            CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
            SwapMMUMode(&mmuMode);
            baseAddr += rowBytes<<1;
            if ( p->flushProcRecord.flushProc ) {
                if ( (result=
            p->flushProcRecord.flushProc(cDataPtr,stripBytes,
            p->flushProcRecord.flushRefCon)) != noErr) {
                    result = codecSpoolErr;
                    goto bail;
                }
            } else {
                cDataPtr += stripBytes;
            }
            if (p->progressProcRecord.progressProc) {
                if ( (result=
                    p->progressProcRecord.progressProc)
                        codecProgressUpdatePercent,
                        FixDiv(y,numStrips),
                        p->progressProcRecord.progressRefCon)
                )   != noErr ) {
                    result = codecAbortErr;
                    goto bail;
                }
            }
        }
    } else {
        SharedGlobals *sg = (*glob)->sharedGlob;
        short tRowBytes = rowBytes<<1;

        SwapMMUMode(&mmuMode);
        for ( y=numStrips; y--; ) {
            CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
            cDataPtr += stripBytes;
            baseAddr += tRowBytes;
        }
        SwapMMUMode(&mmuMode);
    }
}

© 1997 Apple Computer, Inc.

Previous | Chapter Top | Chapter Contents | Next